package repository

import (
	"context"
	"database/sql"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/service/attachment/model"

	"github.com/volatiletech/sqlboiler/v4/boil"
	"github.com/volatiletech/sqlboiler/v4/queries/qm"
)

type (
	// FileInfoRepository is an interface to be customized, add more methods here,
	// and implement the added methods in customFileInfoRepository.
	FileInfoRepository interface {
		FindOne(
			ctx context.Context,
			conn boil.ContextExecutor,
			id uint64,
		) (*model.AttachmentFileInfo, error)
		GetList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.AttachmentFileInfo, int64, error) // ##25.02.11##
		Insert(
			ctx context.Context,
			tx boil.ContextExecutor,
			data *model.AttachmentFileInfo,
		) error // ##25.02.11##
		Update(
			ctx context.Context,
			tx boil.ContextExecutor,
			data *model.AttachmentFileInfo,
		) error // ##25.02.11##
		UpdateBatch(ctx context.Context, tx boil.ContextExecutor, ids []uint64, cols model.M) error
		SoftDelete(ctx context.Context, tx boil.ContextExecutor, id uint64) error
		Restore(ctx context.Context, tx boil.ContextExecutor, id uint64) error
		ForceDelete(ctx context.Context, tx boil.ContextExecutor, id uint64) error
		ForceDeleteBatch(ctx context.Context, tx boil.ContextExecutor, ids []uint64) error
		InsertLog(
			ctx context.Context,
			tx boil.ContextExecutor,
			data *model.AttachmentFileInfoLog,
		) error // ##25.02.11##
	}

	customFileInfoRepository struct {
		mysqlConn *sql.DB
	}
)

// NewFileInfoRepository returns a repository for the database table.
func NewFileInfoRepository(mysqlConn *sql.DB) FileInfoRepository {
	return &customFileInfoRepository{
		mysqlConn: mysqlConn,
	}
}

func (r *customFileInfoRepository) FindOne(
	ctx context.Context,
	conn boil.ContextExecutor,
	id uint64,
) (*model.AttachmentFileInfo, error) {
	var db boil.ContextExecutor = r.mysqlConn
	if conn != nil {
		db = conn
	}
	data, err := model.FindAttachmentFileInfo(ctx, db, id)
	switch err {
	case nil:
		return data, nil
	case sql.ErrNoRows:
		return nil, model.ErrNotFound
	default:
		return nil, err
	}
}

func (r *customFileInfoRepository) GetList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.AttachmentFileInfo, count int64, err error) {
	var page int
	var pageSize int
	var limit int
	var selectCol string
	var mods []qm.QueryMod
	var nextCount int64
	var previousCount int64
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(uint64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(uint64); ok && val > 0 {
				pageSize = int(val)
			}
		case "limit":
			if val, ok := v.(uint32); ok && val > 0 {
				limit = int(val)
			}
		case "offset":
			if withCount == 2 {
				if val, ok := v.(uint32); ok && val > 0 {
					mods = append(mods, qm.Offset(int(val)))
					previousCount = 1
				}
			}
		case "orderBy":
			if val, ok := v.(string); ok && len(val) > 0 {
				mods = append(mods, qm.OrderBy(val))
			}
		case "preMinId":
			if withCount == 3 {
				if val, ok := v.(uint64); ok && val > 0 {
					mods = append(mods, qm.OrderBy(model.AttachmentFileInfoColumns.ID+" DESC"))
					mods = append(mods, model.AttachmentFileInfoWhere.ID.LT(val))
					previousCount = 1
				}
			}
		case "preMaxId":
			if withCount == 4 {
				if val, ok := v.(uint64); ok && val > 0 {
					mods = append(mods, qm.OrderBy(model.AttachmentFileInfoColumns.ID+" ASC"))
					mods = append(mods, model.AttachmentFileInfoWhere.ID.GT(val))
					previousCount = 1
				}
			}
		case "select":
			if withCount != 5 {
				if val, ok := v.(string); ok && len(val) > 0 {
					selectCol = val
				}
			}
		}
	}

	for key, opMap := range filter {
		switch key {
		case model.AttachmentFileInfoColumns.ID:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.ID, opMap)...)
		case model.AttachmentFileInfoColumns.AttachmentID:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.AttachmentID, opMap)...)
		case model.AttachmentFileInfoColumns.ParentID:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.ParentID, opMap)...)
		case model.AttachmentFileInfoColumns.KeyValue:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.KeyValue, opMap)...)
		case model.AttachmentFileInfoColumns.Bucket:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.Bucket, opMap)...)
		case model.AttachmentFileInfoColumns.Size:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.Size, opMap)...)
		case model.AttachmentFileInfoColumns.Hash:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.Hash, opMap)...)
		case model.AttachmentFileInfoColumns.MimeType:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.MimeType, opMap)...)
		case model.AttachmentFileInfoColumns.Type:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.Type, opMap)...)
		case model.AttachmentFileInfoColumns.PutTime:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.PutTime, opMap)...)
		case model.AttachmentFileInfoColumns.RestoreStatus:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.RestoreStatus, opMap)...)
		case model.AttachmentFileInfoColumns.Status:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.Status, opMap)...)
		case model.AttachmentFileInfoColumns.MD5:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.MD5, opMap)...)
		case model.AttachmentFileInfoColumns.EndUser:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.EndUser, opMap)...)
		case model.AttachmentFileInfoColumns.Expiration:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.Expiration, opMap)...)
		case model.AttachmentFileInfoColumns.TransitionToIa:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.TransitionToIa, opMap)...)
		case model.AttachmentFileInfoColumns.TransitionToArchive:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.TransitionToArchive, opMap)...)
		case model.AttachmentFileInfoColumns.TransitionToDeepArchive:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.TransitionToDeepArchive, opMap)...)
		case model.AttachmentFileInfoColumns.PersistentID:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.PersistentID, opMap)...)
		case model.AttachmentFileInfoColumns.Parts:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.Parts, opMap)...)
		case model.AttachmentFileInfoColumns.MetaValue:
			mods = append(mods, modelx.GetStringMods(model.AttachmentFileInfoColumns.MetaValue, opMap)...)
		case model.AttachmentFileInfoColumns.Count:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.Count, opMap)...)
		case model.AttachmentFileInfoColumns.DelStatus:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.DelStatus, opMap)...)
		case model.AttachmentFileInfoColumns.FileType:
			mods = append(mods, modelx.GetIntMods(model.AttachmentFileInfoColumns.FileType, opMap)...)
		}
	}

	if withCount >= 2 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit+1))
		}
	} else if withCount >= 0 {
		count, err = model.AttachmentFileInfos(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 && (withCount == -1 || withCount == 1) {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if len(selectCol) > 0 {
		if selectCol == "," {
			columns = append(columns, model.AttachmentFileInfoColumns.ID)
		} else {
			columns = strings.Split(selectCol, ",")
		}
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.AttachmentFileInfos(mods...).All(ctx, r.mysqlConn)
	if err != nil {
		return
	}

	if withCount >= 2 {
		if limit > 0 && len(list) > limit {
			nextCount = 1
			list = list[:limit]
		}
		count = previousCount + nextCount*2
	}

	return
}

func (r *customFileInfoRepository) Insert(
	ctx context.Context,
	tx boil.ContextExecutor,
	data *model.AttachmentFileInfo,
) error {
	var db boil.ContextExecutor = r.mysqlConn
	if tx != nil {
		db = tx
	}

	return data.Insert(ctx, db, boil.Infer())
}

func (r *customFileInfoRepository) Update(
	ctx context.Context,
	tx boil.ContextExecutor,
	data *model.AttachmentFileInfo,
) error {
	var db boil.ContextExecutor = r.mysqlConn
	if tx != nil {
		db = tx
	}

	_, err := data.Update(ctx, db, boil.Blacklist(
		model.AttachmentFileInfoColumns.ID,
	))
	if err != nil {
		return err
	}

	return nil
}

func (r *customFileInfoRepository) UpdateBatch(
	ctx context.Context,
	tx boil.ContextExecutor,
	ids []uint64,
	cols model.M,
) error {
	if len(ids) > 0 {
		var db boil.ContextExecutor = r.mysqlConn
		if tx != nil {
			db = tx
		}

		var mods []qm.QueryMod
		mods = append(mods, model.AttachmentFileInfoWhere.ID.IN(ids))

		rowsAff, err := model.AttachmentFileInfos(mods...).UpdateAll(ctx, db, cols)
		if err != nil {
			return err
		}

		if rowsAff == 0 {
			return model.ErrNotUpdated
		}
	}

	return nil
}

func (r *customFileInfoRepository) SoftDelete(
	ctx context.Context,
	tx boil.ContextExecutor,
	id uint64,
) error {
	var db boil.ContextExecutor = r.mysqlConn
	if tx != nil {
		db = tx
	}

	data, err := r.FindOne(ctx, db, id)
	if err != nil {
		if err == model.ErrNotFound {
			return nil
		}

		return err
	}

	if data.DelStatus == uint8(globalkey.StatusDeleted) {
		return nil
	}

	data.DelStatus = uint8(globalkey.StatusDeleted)

	rowsAff, err := data.Update(ctx, db, boil.Blacklist(
		model.AttachmentFileInfoColumns.ID,
	))
	if err != nil {
		return err
	}

	if rowsAff == 0 {
		return model.ErrNotUpdated
	}

	return nil
}

func (r *customFileInfoRepository) Restore(
	ctx context.Context,
	tx boil.ContextExecutor,
	id uint64,
) error {
	var db boil.ContextExecutor = r.mysqlConn
	if tx != nil {
		db = tx
	}

	data, err := r.FindOne(ctx, db, id)
	if err != nil {
		if err == model.ErrNotFound {
			return nil
		}

		return err
	}

	if data.DelStatus != uint8(globalkey.StatusDeleted) {
		return nil
	}

	data.DelStatus = uint8(globalkey.StatusPending)

	rowsAff, err := data.Update(ctx, db, boil.Blacklist(
		model.AttachmentFileInfoColumns.ID,
	))
	if err != nil {
		return err
	}

	if rowsAff == 0 {
		return model.ErrNotUpdated
	}

	return nil
}

func (r *customFileInfoRepository) ForceDelete(
	ctx context.Context,
	tx boil.ContextExecutor,
	id uint64,
) error {
	var db boil.ContextExecutor = r.mysqlConn
	if tx != nil {
		db = tx
	}

	data, err := r.FindOne(ctx, db, id)
	if err != nil {
		if err == model.ErrNotFound {
			return nil
		}

		return err
	}

	rowsAff, err := data.Delete(ctx, db)
	if err != nil {
		return err
	}

	if rowsAff == 0 {
		return model.ErrNotDeleted
	}

	return nil
}

func (r *customFileInfoRepository) ForceDeleteBatch(
	ctx context.Context,
	tx boil.ContextExecutor,
	ids []uint64,
) error {
	if len(ids) > 0 {
		var db boil.ContextExecutor = r.mysqlConn
		if tx != nil {
			db = tx
		}

		var mods []qm.QueryMod
		mods = append(mods, model.AttachmentFileInfoWhere.ID.IN(ids))

		rowsAff, err := model.AttachmentFileInfos(mods...).DeleteAll(ctx, db)
		if err != nil {
			return err
		}

		if rowsAff == 0 {
			return model.ErrNotDeleted
		}

		return nil
	}

	return nil
}

func (r *customFileInfoRepository) InsertLog(
	ctx context.Context,
	tx boil.ContextExecutor,
	data *model.AttachmentFileInfoLog,
) error {
	var db boil.ContextExecutor = r.mysqlConn
	if tx != nil {
		db = tx
	}

	return data.Insert(ctx, db, boil.Infer())
}
